#include "slide_show.hpp"
#include <string>
#include <sstream>
#include <fstream>
#include <rapidxml/rapidxml_print.hpp>
#include <Box2D/Box2D.h>
#include <wx/wx.h>
#include <resource_cache/resource_cache.hpp>
#include <boost/shared_ptr.hpp>
#include <utils/from_string.hpp>
#include <boost/shared_array.hpp>
#include <wx/wxprec.h>
#include "../string_conv.h"
#include "tiny/tinyxml.h"

slide_show g_slides;

slide_show::~slide_show()
{
	g_cache.unload_images();
}

void slide_show::clear()
{
	slides.clear();
	current_slide = -1;
	active_body_pos = false;
	active_kine_pos = false;
	active_vertex_pos = false;
	active_circle_pos = false;
	appending_kine_points = false;
	appending_vertex_points = false;
	g_cache.unload_images();
}
using namespace rapidxml;


bool slide_show::save_to_file( std::string filepath )
{
	TiXmlDocument doc( filepath.c_str() );
	if( doc.LoadFile() )
		doc.Clear();

	TiXmlNode* node;

	// NEED TO ADD ONE OF THESE
	//TiXmlDeclaration decl( "1.0", "utf-8", "" );
	//doc.InsertEndChild( decl );

	{	// add the root node
		TiXmlElement newNode("level" );

		doc.InsertEndChild( newNode );
		node = doc.FirstChildElement( "level" );	// point node to the root node
		if( !node )
			return false;
	}

	for( unsigned int xslide = 0; xslide < g_slides.slides.size(); ++xslide )
	{
		slide cs = g_slides.slides[xslide];

		TiXmlElement newNode( "slide" );
		newNode.ToElement()->SetAttribute( "name", &cs.name[0] );

		TiXmlElement gravNode( "gravity" );
		std::string grav_str = stringify(cs.gravity.x) + " " + stringify(cs.gravity.y) ;
		gravNode.InsertEndChild(TiXmlText( &grav_str[0] ));
		newNode.InsertEndChild( gravNode );

		TiXmlElement bg_node( "image" );
		if( cs.background_file.length() > 0 )
		{
			bg_node.InsertEndChild( TiXmlText( &cs.background_file[0] ) );
			newNode.InsertEndChild( bg_node );
		}

		for( unsigned int xbody = 0; xbody < g_slides.slides[xslide].bodys.size(); ++xbody )
		{
			body & cb = cs.bodys[xbody];

			TiXmlElement b_node( "body" );
			if(cb.name.length() > 0 )
				b_node.SetAttribute( "name", &cb.name[0] );
			else
			{
				std::string name_s = "no_name";
				b_node.SetAttribute( "name", &name_s[0] );
			}

			std::string type_str = "static";
			if( cb.body_type == body_dynamic )
				type_str = "dynamic";
			else if( cb.body_type == body_kinematic )
				type_str = "kinematic";
			b_node.SetAttribute( "type", &type_str[0] );
			
			if( cb.animation_file.length() > 0 )
			{
				TiXmlElement a_node( "animation" );
				a_node.InsertEndChild( TiXmlText( &cb.animation_file[0] ) );
				b_node.InsertEndChild( a_node );
			}

			if( cb.body_type == body_static || cb.body_type == body_dynamic )
			{
				{TiXmlElement a_node( "angle" );
				std::string temp_str = stringify(cb.angle);
				a_node.InsertEndChild( TiXmlText( &temp_str[0] ) );
				b_node.InsertEndChild( a_node );}

				{TiXmlElement a_node( "position" );
				std::string temp_str = stringify(cb.position.x) + " " + stringify(cb.position.y) ;
				a_node.InsertEndChild(TiXmlText( &temp_str[0] ));
				b_node.InsertEndChild( a_node );}
			}
			if( cb.body_type == body_dynamic )
			{
				{TiXmlElement a_node( "angular_dampening" );
				std::string temp_str = stringify(cb.angular_dampening);
				a_node.InsertEndChild( TiXmlText( &temp_str[0] ) );
				b_node.InsertEndChild( a_node );}
				{TiXmlElement a_node( "dampening" );
				std::string temp_str = stringify(cb.dampening);
				a_node.InsertEndChild( TiXmlText( &temp_str[0] ) );
				b_node.InsertEndChild( a_node );}
				{TiXmlElement a_node( "fixed_rotation" );
				std::string temp_str = "0";
				if( cb.fixed_angle )
					temp_str = "1";
				a_node.InsertEndChild( TiXmlText( &temp_str[0] ) );
				b_node.InsertEndChild( a_node );}
			}
			else if( cb.body_type == body_kinematic )
			{
				TiXmlElement points_node( "points" );
				
				for( unsigned int k = 0; k < cb.kinematic_points.size(); ++k )
				{
					TiXmlElement p_node( "point" );

					{TiXmlElement a_node( "position" );
					std::string temp_str = stringify(cb.kinematic_points[k].point.x) + " " + stringify(cb.kinematic_points[k].point.y) ;
					a_node.InsertEndChild(TiXmlText( &temp_str[0] ));
					p_node.InsertEndChild( a_node );}
					{TiXmlElement a_node( "time" );
					std::string temp_str = stringify(cb.kinematic_points[k].time);
					a_node.InsertEndChild( TiXmlText( &temp_str[0] ) );
					p_node.InsertEndChild( a_node );}
					{TiXmlElement a_node( "angle" );
					std::string temp_str = stringify(cb.kinematic_points[k].angle);
					a_node.InsertEndChild( TiXmlText( &temp_str[0] ) );
					p_node.InsertEndChild( a_node );}					

					points_node.InsertEndChild( p_node );
				}
				b_node.InsertEndChild( points_node );
			}

			if( !cb.shapes.empty() )
			{
				TiXmlElement shapes_node( "shapes" );
				for( unsigned int s = 0; s < cb.shapes.size(); ++s )
				{
					TiXmlElement s_node( "shape" );

					{TiXmlElement a_node( "density" );
					std::string temp_str = stringify(cb.shapes[s].density);
					a_node.InsertEndChild( TiXmlText( &temp_str[0] ) );
					s_node.InsertEndChild( a_node );}
					{TiXmlElement a_node( "friction" );
					std::string temp_str = stringify(cb.shapes[s].friction);
					a_node.InsertEndChild( TiXmlText( &temp_str[0] ) );
					s_node.InsertEndChild( a_node );}
					{TiXmlElement a_node( "restitution" );
					std::string temp_str = stringify(cb.shapes[s].restitution);
					a_node.InsertEndChild( TiXmlText( &temp_str[0] ) );
					s_node.InsertEndChild( a_node );}
					{TiXmlElement a_node( "on_collide_messages" );
					std::string colM = " ";
					if( !cb.shapes[s].on_collide_message.empty() )
						a_node.InsertEndChild( TiXmlText( &cb.shapes[s].on_collide_message[0] ) );
					else
						a_node.InsertEndChild( TiXmlText( &colM[0] ) );
					s_node.InsertEndChild( a_node );}
					{TiXmlElement a_node( "is_sensor" );
					std::string sensor = "0";
					if(cb.shapes[s].is_sensor)
						sensor = "1";
					a_node.InsertEndChild( TiXmlText( &sensor[0] ) );
					s_node.InsertEndChild( a_node );}

					if( cb.shapes[s].shape_type == shape_circle )
					{
						{TiXmlElement a_node( "position_offset" );
						std::string temp_str = stringify(cb.shapes[s].circle_offset.x) + " " + stringify(cb.shapes[s].circle_offset.y);
						a_node.InsertEndChild( TiXmlText( &temp_str[0] ) );
						s_node.InsertEndChild( a_node );}
						{TiXmlElement a_node( "radius" );
						std::string temp_str = stringify(cb.shapes[s].circle_radius);
						a_node.InsertEndChild( TiXmlText( &temp_str[0] ) );
						s_node.InsertEndChild( a_node );}

						std::string type_str = "circle";
						s_node.SetAttribute( "type", &type_str[0] );
					}
					else
					{
						std::string type_str = "polygon";
						s_node.SetAttribute( "type", &type_str[0] );

						TiXmlElement verts_node( "vertices" );

						for( unsigned int v = 0; v < cb.shapes[s].polygon_points.size(); ++v )
						{
							{TiXmlElement a_node( "vertex" );
							std::string temp_str = stringify(cb.shapes[s].polygon_points[v].x) + " " + stringify(cb.shapes[s].polygon_points[v].y);
							a_node.InsertEndChild( TiXmlText( &temp_str[0] ) );
							verts_node.InsertEndChild( a_node );}
						}

						s_node.InsertEndChild( verts_node );
					}
					shapes_node.InsertEndChild( s_node );
				}
				b_node.InsertEndChild(shapes_node);
			}
			newNode.InsertEndChild( b_node );
		}
		node->InsertEndChild( newNode );
	}

	doc.SaveFile( filepath.c_str() );

	return true;
}

bool slide_show::load_from_xml( std::string filepath )
{
	std::ifstream file( filepath.c_str( ) );

	if ( !file.is_open( ) )
	{
		throw std::runtime_error( filepath + " could not be opened." );
	}

	file.seekg( 0, std::ios::end );
	size_t length = file.tellg( );
	file.seekg( 0, std::ios::beg );

	boost::shared_array< char > text( new char[ length + 1 ] );
	memset( text.get( ), 0, sizeof( char ) * ( length + 1 ) );

	file.read( text.get( ), length );
	rapidxml::xml_document<> document; document.parse<0>( text.get( ) );

	rapidxml::xml_node<> * level_node = document.first_node( "level" );
	if ( level_node == nullptr )
		throw std::runtime_error( filepath + " is invalid." );
	
	for (rapidxml::xml_node<> * slide_node = level_node->first_node( "slide" ); slide_node; slide_node = slide_node->next_sibling( "slide" ) )
	{
		slide current_slide;
		rapidxml::xml_attribute<> *name_att = slide_node->first_attribute("name");
		if( name_att )
			current_slide.name = name_att->value();

		for( rapidxml::xml_node<> * slide_sub_node = slide_node->first_node(); slide_sub_node; slide_sub_node = slide_sub_node->next_sibling() )
		{
			std::string node_type = slide_sub_node->name();
			if( node_type == "body" )
			{
				body current_body;

				rapidxml::xml_attribute<> *name_att = slide_sub_node->first_attribute("name");
				if( name_att )
					current_body.name = name_att->value();

				rapidxml::xml_attribute<> *type_att = slide_sub_node->first_attribute("type");
				if( type_att )
				{
					std::string type = type_att->value();
					if( type == "static" )
						current_body.body_type = body_static;
					else if( type == "kinematic" )
						current_body.body_type = body_kinematic;
					else if( type == "dynamic" )
						current_body.body_type = body_dynamic;
				}

				for (rapidxml::xml_node<> * body_sub_node = slide_sub_node->first_node(); body_sub_node; body_sub_node = body_sub_node->next_sibling() )
				{
					std::string node_type = body_sub_node->name();
					if( node_type == "shapes" )
					{
						for (rapidxml::xml_node<> * shape_node = body_sub_node->first_node("shape"); shape_node; shape_node = shape_node->next_sibling("shape") )
						{
							rapidxml::xml_attribute<> *shape_type = shape_node->first_attribute("type");
							shape current_shape;
							if( shape_type )
							{
								std::string type = shape_type->value();
								if( type == "circle" )
									current_shape.shape_type = shape_circle;
								else if( type == "polygon" )
									current_shape.shape_type = shape_polygon;
							}
							// Shapes sub nodes
							for (rapidxml::xml_node<> * shape_sub_node = shape_node->first_node(); shape_sub_node; shape_sub_node = shape_sub_node->next_sibling() )
							{
								std::string shape_node_name = shape_sub_node->name();

								if( shape_node_name == "position_offset" )
								{
									b2Vec2 pos = from_string< b2Vec2 >( shape_sub_node->value( ) );
									current_shape.circle_offset = pos;
								}
								else if( shape_node_name == "radius" )
									current_shape.circle_radius = from_string< float >( shape_sub_node->value( ) );
								else if( shape_node_name == "vertices" )
								{
									for (rapidxml::xml_node<> * vert_node = shape_sub_node->first_node("vertex"); vert_node; vert_node = vert_node->next_sibling("vertex") )
									{
										b2Vec2 pos = from_string< b2Vec2 >( vert_node->value( ) );
										current_shape.polygon_points.push_back(pos);
									}
								}
								else if( shape_node_name == "on_collide_messages" ) // TODO split into separate messages
									current_shape.on_collide_message = shape_sub_node->value();
								else if( shape_node_name == "is_sensor" )
								{
									int sen = from_string<int>( shape_sub_node->value() );
									if( sen == 1 )
										current_shape.is_sensor = true;
									else
										current_shape.is_sensor = false;
								}
								else if( shape_node_name == "density" )
									current_shape.density = from_string< float >( shape_sub_node->value( ) );
								else if( shape_node_name == "friction" )
									current_shape.friction = from_string< float >( shape_sub_node->value( ) );
								else if( shape_node_name == "restitution" )
									current_shape.restitution = from_string< float >( shape_sub_node->value( ) );
							}
							current_body.shapes.push_back(current_shape);
						}
					}
					else if( node_type == "position" )
					{
						b2Vec2 pos = from_string< b2Vec2 >( body_sub_node->value( ) );
						current_body.position = pos;
					}
					else if( node_type == "angle" )
					{
						current_body.angle = from_string<float>( body_sub_node->value() );
					}
					else if( node_type == "animation" )
					{
						wxString anim = wxString(body_sub_node->value());
						current_body.animation_file = anim;
					}
					else if( node_type == "dampening" )
					{
						current_body.dampening = from_string<float>( body_sub_node->value() );
					}
					else if( node_type == "angular_dampening" )
					{
						current_body.angular_dampening = from_string<float>( body_sub_node->value() );
					}
					else if( node_type == "fixed_rotation" )
					{
						int fr = from_string<int>( body_sub_node->value() );
						if( fr == 1 )
							current_body.fixed_angle = true;
						else
							current_body.fixed_angle = false;
					}
					else if( node_type == "points" )
					{
						for (rapidxml::xml_node<> * point_node = body_sub_node->first_node("point"); point_node; point_node = point_node->next_sibling("point") )
						{
							kine_point p;

							rapidxml::xml_node<> * pos_node = point_node->first_node("position");
							if( !pos_node )
								return false;

							b2Vec2 pos = from_string< b2Vec2 >( pos_node->value( ) );
							p.point.x = pos.x;	p.point.y = pos.y;

							rapidxml::xml_node<> * t_node = point_node->first_node("time");
							if( !t_node )
								return false;
							p.time = from_string<float>( t_node->value() );

							rapidxml::xml_node<> * a_node = point_node->first_node("angle");
							if( !a_node )
								return false;
							p.angle = from_string<float>( a_node->value() );

							current_body.kinematic_points.push_back(p);
						}
					}
				}
				current_slide.bodys.push_back(current_body);
			}
			else if( node_type == "joint" )
			{
			}
			else if( node_type == "image" )
			{
				std::string bg = slide_sub_node->value();
				current_slide.set_background( bg );
			}
			else if( node_type == "gravity" )
			{
				b2Vec2 grav = from_string< b2Vec2 >( slide_sub_node->value( ) );
				current_slide.gravity.x = grav.x;
				current_slide.gravity.y = grav.y;
			}
		}
		slides.push_back(current_slide);
	}

	file.close();
	return true;
}

//bool load_body( rapidxml::xml_node<> * body_node )
//{
//	body current_body;
//
//	rapidxml::xml_attribute<> *name_att = body_node->first_attribute("name");
//	if( name_att )
//		current_body.name = wxString(name_att->value());
//
//	rapidxml::xml_attribute<> *type_att = body_node->first_attribute("type");
//	if( type_att )
//	{
//		std::string type = name_att->value();
//		if( type == "static" )
//			current_body.body_type = body_static;
//		else if( type == "kinematic" )
//			current_body.body_type = body_kinematic;
//		else if( type == "dynamic" )
//			current_body.body_type = body_dynamic;
//	}
//
//	for (rapidxml::xml_node<> * body_sub_node = body_node->first_node(); body_sub_node; body_sub_node = body_sub_node->next_sibling() )
//	{
//		std::string node_type = body_sub_node->name();
//		if( node_type == "shapes" )
//		{
//			if( !load_body(body_sub_node) )
//				return false;
//		}
//		else if( node_type == "position" )
//		{
//			b2Vec2 pos = from_string< b2Vec2 >( body_sub_node->value( ) );
//			current_body.position.x = pos.x;
//			current_body.position.y = pos.y;
//		}
//		else if( node_type == "angle" )
//		{
//			current_body.angle = from_string<float>( body_sub_node->value() );
//		}
//		else if( node_type == "animation" )
//		{
//			wxString anim = wxString(body_sub_node->value());
//			current_body.animation_file = anim;
//		}
//		else if( node_type == "dampening" )
//		{
//			current_body.dampening = from_string<float>( body_sub_node->value() );
//		}
//		else if( node_type == "angular_dampening" )
//		{
//			current_body.angular_dampening = from_string<float>( body_sub_node->value() );
//		}
//		else if( node_type == "fixed_rotation" )
//		{
//		}
//		else if( node_type == "points" )
//		{
//			//if( !load_body(body_sub_node) )
//					//return false;
//		}
//	}
//
//	return true;
//}
bool load_shape( rapidxml::xml_node<> * shape_node )
{
	return true;
}